package metadata;

import record.Layout;
import record.TableScan;
import tx.Transaction;

import java.util.HashMap;
import java.util.Map;

/**
 * The statistics manager is responsible for
 * keeping statistical information about each table.
 * The manager does not store this information in the database.
 * Instead, it calculates this information on system startup,
 * and periodically refreshes it.
 * @author Edward Sciore
 */
public class StatMgr {

    private TableMgr tblMgr;
    private Map<String, StatInfo> tablestats;
    private int numcalls;

    /**
     * Create the statistics manager.
     * The initial statistics are calculated by
     * traversing the entire database.
     * @param tx the startup transaction
     */
    public StatMgr(TableMgr tblMgr, Transaction tx) {
        this.tblMgr = tblMgr;
        refreshStatistics(tx);
    }

    public synchronized StatInfo getStatInfo(String tblname,
                                             Layout layout, Transaction tx) {
        numcalls++;
        if (numcalls > 100)
            refreshStatistics(tx);
        StatInfo si = tablestats.computeIfAbsent(tblname,key -> calcTableStats(tblname, layout, tx));
        return si;
    }

    private synchronized void refreshStatistics(Transaction tx) {
        tablestats = new HashMap<>();
        numcalls = 0;
        Layout tcatlayout = tblMgr.getLayout("tblcat", tx);
        TableScan tcat = new TableScan(tx, "tblcat", tcatlayout);
        while (tcat.next()) {
            String tblname = tcat.getString("tblname");
            Layout layout = tblMgr.getLayout(tblname, tx);
            StatInfo si = calcTableStats(tblname, layout, tx);
            tablestats.put(tblname, si);
        }
        tcat.close();
    }

    private synchronized StatInfo calcTableStats(String tblname, Layout layout, Transaction tx) {
        int numRecs = 0;
        int numblocks = 0;
        TableScan ts = new TableScan(tx, tblname, layout);
        while (ts.next()) {
            numRecs++;
            numblocks = ts.getRid().blockNumber() + 1;
        }
        ts.close();
        return new StatInfo(numblocks, numRecs);
    }
}
